import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import LineString from 'ol/geom/LineString';
import Polygon from 'ol/geom/Polygon';
import Circle from 'ol/geom/Circle';
import { getArea, getLength } from 'ol/sphere';
import { distance as coordinateDistance } from 'ol/coordinate';

/**
 * 空间分析工具类，提供各种空间计算和分析功能
 * 用于支持企业级业务场景的空间决策分析
 */
export class SpatialAnalysis {
  /**
   * 计算两点之间的距离
   * @param point1 第一个点的坐标 [lon, lat]
   * @param point2 第二个点的坐标 [lon, lat]
   * @param units 单位，默认'm'（米），可选'km'（公里）、'miles'（英里）
   * @returns 距离值
   */
  static distance(point1: [number, number], point2: [number, number], units: 'm' | 'km' | 'miles' = 'm'): number {
    const meters = coordinateDistance(point1, point2);
    
    switch (units) {
      case 'km':
        return meters / 1000;
      case 'miles':
        return meters * 0.000621371;
      default:
        return meters;
    }
  }

  /**
   * 计算多点之间的总距离（路径长度）
   * @param coordinates 坐标数组 [[lon1, lat1], [lon2, lat2], ...]
   * @param units 单位，默认'm'（米）
   * @param projection 坐标系，默认'EPSG:4326'
   * @returns 总距离
   */
  static measureDistance(
    coordinates: [number, number][],
    units: 'm' | 'km' | 'miles' = 'm',
    projection: string = 'EPSG:4326'
  ): number {
    if (coordinates.length < 2) return 0;
    
    const lineString = new LineString(coordinates);
    const length = getLength(lineString, {
      projection: projection,
      radius: 6378137 // 地球半径
    });
    
    switch (units) {
      case 'km':
        return length / 1000;
      case 'miles':
        return length * 0.000621371;
      default:
        return length;
    }
  }

  /**
   * 计算多边形的面积
   * @param coordinates 多边形坐标数组
   * @param units 单位，默认'm²'（平方米），可选'km²'（平方公里）、'ha'（公顷）
   * @param projection 坐标系，默认'EPSG:4326'
   * @returns 面积值
   */
  static measureArea(
    coordinates: [number, number][],
    units: 'm²' | 'km²' | 'ha' = 'm²',
    projection: string = 'EPSG:4326'
  ): number {
    if (coordinates.length < 3) return 0;
    
    // 确保多边形是闭合的
    const closedCoords = [...coordinates];
    if (!this.arePointsEqual(closedCoords[0], closedCoords[closedCoords.length - 1])) {
      closedCoords.push(closedCoords[0]);
    }
    
    const polygon = new Polygon([closedCoords]);
    const area = getArea(polygon, {
      projection: projection,
      radius: 6378137
    });
    
    switch (units) {
      case 'km²':
        return area / 1000000;
      case 'ha':
        return area / 10000;
      default:
        return area;
    }
  }

  /**
   * 缓冲区分析
   * @param point 中心点坐标
   * @param radius 缓冲半径（单位：米）
   * @param segments 缓冲区分段数
   * @returns 缓冲区域的坐标数组
   */
  static buffer(
    point: [number, number],
    radius: number,
    segments: number = 64
  ): [number, number][] {
    const coordinates: number[][] = [];
    const centerX = point[0];
    const centerY = point[1];
    
    // 生成圆的多边形顶点
    for (let i = 0; i <= segments; i++) {
      const angle = (i / segments) * Math.PI * 2;
      const x = centerX + radius * Math.cos(angle);
      const y = centerY + radius * Math.sin(angle);
      coordinates.push([x, y]);
    }
    
    // 创建多边形
    const polygon = new Polygon([coordinates]);
    
    return polygon.getCoordinates()[0] as [number, number][];
  }

  /**
   * 判断点是否在多边形内
   * @param point 点坐标 [lon, lat]
   * @param polygon 多边形坐标数组 [[lon1, lat1], [lon2, lat2], ...]
   * @returns 是否在多边形内
   */
  static isPointInPolygon(point: [number, number], polygon: [number, number][]): boolean {
    const x = point[0];
    const y = point[1];
    let inside = false;
    
    for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
      const xi = polygon[i][0];
      const yi = polygon[i][1];
      const xj = polygon[j][0];
      const yj = polygon[j][1];
      
      const intersect = 
        ((yi > y) !== (yj > y)) &&
        (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
      
      if (intersect) inside = !inside;
    }
    
    return inside;
  }

  /**
   * 判断两条线是否相交
   * @param line1 第一条线的坐标数组
   * @param line2 第二条线的坐标数组
   * @returns 是否相交
   */
  static doLinesIntersect(
    line1: [number, number][],
    line2: [number, number][]
  ): boolean {
    // 简化实现：检查线段对之间的相交
    for (let i = 0; i < line1.length - 1; i++) {
      for (let j = 0; j < line2.length - 1; j++) {
        if (this.segmentsIntersect(line1[i], line1[i + 1], line2[j], line2[j + 1])) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * 计算多边形的中心点
   * @param polygon 多边形坐标数组
   * @returns 中心点坐标
   */
  static getPolygonCentroid(polygon: [number, number][]): [number, number] {
    let x = 0, y = 0, area = 0;
    const n = polygon.length;
    
    for (let i = 0; i < n; i++) {
      const j = (i + 1) % n;
      const xi = polygon[i][0];
      const yi = polygon[i][1];
      const xj = polygon[j][0];
      const yj = polygon[j][1];
      
      const a = xi * yj - xj * yi;
      x += (xi + xj) * a;
      y += (yi + yj) * a;
      area += a;
    }
    
    area *= 0.5;
    
    if (area !== 0) {
      x /= 6 * area;
      y /= 6 * area;
    }
    
    return [x, y];
  }

  /**
   * 计算最近的点
   * @param target 目标点坐标
   * @param points 点数组
   * @returns 最近点的索引和距离
   */
  static findNearestPoint(
    target: [number, number],
    points: [number, number][]
  ): { index: number; distance: number } {
    let minDistance = Infinity;
    let nearestIndex = -1;
    
    points.forEach((point, index) => {
      const dist = this.distance(target, point);
      if (dist < minDistance) {
        minDistance = dist;
        nearestIndex = index;
      }
    });
    
    return { index: nearestIndex, distance: minDistance };
  }

  /**
   * 辅助方法：判断两个线段是否相交
   */
  private static segmentsIntersect(
    p1: [number, number],
    p2: [number, number],
    p3: [number, number],
    p4: [number, number]
  ): boolean {
    // 简化的线段相交判断
    const orientation = (p: [number, number], q: [number, number], r: [number, number]) => {
      const val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
      if (val === 0) return 0; // 共线
      return val > 0 ? 1 : 2; // 顺时针或逆时针
    };
    
    const o1 = orientation(p1, p2, p3);
    const o2 = orientation(p1, p2, p4);
    const o3 = orientation(p3, p4, p1);
    const o4 = orientation(p3, p4, p2);
    
    // 一般情况
    if (o1 !== o2 && o3 !== o4) return true;
    
    // 共线情况（简化处理）
    return false;
  }

  /**
   * 辅助方法：判断两个点是否相等
   */
  private static arePointsEqual(p1: [number, number], p2: [number, number]): boolean {
    return Math.abs(p1[0] - p2[0]) < 1e-10 && Math.abs(p1[1] - p2[1]) < 1e-10;
  }
}

/**
 * 物流专用分析工具类，提供物流场景下的空间分析功能
 */
export class LogisticsAnalyzer {
  /**
   * 计算最优配送路径（简化版TSP）
   * @param startPoint 起点坐标
   * @param destinations 目的地坐标数组
   * @returns 优化后的路径点索引顺序
   */
  static optimizeRoutes(
    startPoint: [number, number],
    destinations: [number, number][]
  ): number[] {
    // 使用最近邻算法（简化版，实际应使用更复杂的算法）
    const path: number[] = [];
    const unvisited: Set<number> = new Set(Array.from({ length: destinations.length }, (_, i) => i));
    
    let current = startPoint;
    
    while (unvisited.size > 0) {
      let nearestIndex = -1;
      let minDistance = Infinity;
      
      unvisited.forEach(index => {
        const dist = SpatialAnalysis.distance(current, destinations[index]);
        if (dist < minDistance) {
          minDistance = dist;
          nearestIndex = index;
        }
      });
      
      if (nearestIndex !== -1) {
        path.push(nearestIndex);
        unvisited.delete(nearestIndex);
        current = destinations[nearestIndex];
      }
    }
    
    return path;
  }

  /**
   * 计算区域内的配送中心覆盖范围
   * @param centers 配送中心坐标数组
   * @param radius 覆盖半径（米）
   * @returns 各中心的覆盖多边形
   */
  static calculateCoverage(
    centers: [number, number][],
    radius: number
  ): [number, number][][] {
    return centers.map(center => SpatialAnalysis.buffer(center, radius));
  }

  /**
   * 分析多点是否在指定的禁运区域内
   * @param points 点坐标数组
   * @param forbiddenZones 禁运区域多边形数组
   * @returns 每个点是否在禁运区域内
   */
  static checkForbiddenZones(
    points: [number, number][],
    forbiddenZones: [number, number][][]
  ): boolean[] {
    return points.map(point => 
      forbiddenZones.some(zone => SpatialAnalysis.isPointInPolygon(point, zone))
    );
  }
}
